﻿using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System;

namespace MyAssetBundles {
    public class AnalysisFolder : BaseFolder, IFolder {
        protected List<string> _extensionList;
        public void Generate (string path, IFolderProgress progress)
        {
            Reset();
            _extensionList = MyAssetBundlesConfig.GetExtensionWithUpdateMode(_updateMode);
            if (MyAssetBundlesUtil.ExtensionEquals(path, MyAssetBundlesConfig.PackageExtension))
                ParseCompressFile(path, progress);
            else
                ParseFolder(path, progress);
        }

        protected void ParseCompressFile (string path, IFolderProgress progress)
        {
            FileStream fStream = new FileStream(path, FileMode.Open, FileAccess.Read);
            if (fStream.Length < 8) return;
            byte[] content = new byte[1024];
            fStream.Read(content, 0, 8);
            int fileCount = BitConverter.ToInt32(content, 0);
            int singleDesByteNum = BitConverter.ToInt32(content, 4);

            int desByteNum = fileCount * singleDesByteNum + 8;
            if (fStream.Length < desByteNum) return;
            byte[] desByte = new byte[desByteNum];
            fStream.Read(desByte, 0, desByteNum);
            for (int i = 0; i < fileCount; i++)
            {
                MyFileInfo myFile = new MyFileInfo();
                myFile.IsFromCompressFile = true;
                myFile.Path = path;

                myFile.NameCursorStart = BitConverter.ToInt32(desByte, i * singleDesByteNum);
                int nameLength = BitConverter.ToInt32(desByte, i * singleDesByteNum + 4);
                myFile.MD5CursorStart = BitConverter.ToInt32(desByte, i * singleDesByteNum + 8);
                int MD5Length = BitConverter.ToInt32(desByte, i * singleDesByteNum + 12);
                myFile.DataCursorStart = BitConverter.ToInt32(desByte, i * singleDesByteNum + 16);
                myFile.DataByteLength = BitConverter.ToInt32(desByte, i * singleDesByteNum + 20);

                myFile.NameByte = new byte[nameLength];
                fStream.Seek(myFile.NameCursorStart, SeekOrigin.Begin);
                fStream.Read(myFile.NameByte, 0, nameLength);
                myFile.Name = Encoding.UTF8.GetString(myFile.NameByte);

                myFile.MD5Byte = new byte[MD5Length];
                fStream.Seek(myFile.MD5CursorStart, SeekOrigin.Begin);
                fStream.Read(myFile.MD5Byte, 0, MD5Length);
                myFile.MD5String = MyAssetBundlesUtil.MD5ToString(myFile.MD5Byte);
                
                Add(myFile.Name, myFile);
            }
            fStream.Close();
        }

        protected void ParseFolder (string path, IFolderProgress progress)
        {
            DirectoryInfo dir = new DirectoryInfo(path);
            List<string> filesList = new List<string>();
            CollectFiles(ref filesList, dir, true);

            long processNum = 0, totalNum = filesList.Count;
            SetProgress(progress, totalNum);
            List<string>.Enumerator etor = filesList.GetEnumerator();
            while (etor.MoveNext())
            {
                FileInfo file = new FileInfo(etor.Current);
                if (file.Exists)
                {
                    MyFileInfo myFile = new MyFileInfo();
                    myFile.Path = MyAssetBundlesUtil.PathNormalize(file.FullName);
                    myFile.Name = MyAssetBundlesUtil.PathNormalize(file.FullName.Replace(dir.FullName, ""), true);
                    myFile.NameByte = Encoding.UTF8.GetBytes(myFile.Name);
                    FileStream fStream = file.OpenRead();
                    myFile.DataByteLength = (int)fStream.Length;
                    myFile.MD5Byte = MD5.Create().ComputeHash(fStream);
                    myFile.MD5String = MyAssetBundlesUtil.MD5ToString(myFile.MD5Byte);
                    fStream.Close();
                    Add(myFile.Name, myFile);
                }

                SetProcessNum(++processNum);
            }
        }

        protected void CollectFiles (ref List<string> filesList, DirectoryInfo dirInfo, bool manifest)
        {
            if (_extensionList == null || _extensionList.Count == 0) return;
            foreach (var file in dirInfo.GetFiles())
            {
                if (_extensionList.BinarySearch(file.Extension) < 0) continue;
                filesList.Add(file.FullName);
                if (manifest)
                    filesList.Add(file.FullName + MyAssetBundlesConfig.ManifestExtension);
            }

            foreach (var dirTemp in dirInfo.GetDirectories())
            {
                CollectFiles(ref filesList, dirTemp, manifest);
            }
        }
    }
}

